package com.yonyou.distribute.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * p27、p28
 * 给锁添加过期时间
 * 防止误删锁
 */
@Slf4j
@Service
public class RedisService_4 {

    @Resource
    private StringRedisTemplate stringRedisTemplate;



    /**
     * 解决宕机 添加锁的过期时机，但是时间不合理，可能不够
     * 这个过期处理宕机，但是业务时间不一定够
     *
     * 处理防止误删
     *
     */
    @Transactional
    public void deduct() {
        String uuid = UUID.randomUUID().toString();
        while (!this.stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 1, TimeUnit.SECONDS)){
            // 循环重试
            try{
                Thread.sleep(50);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        // 这时候获取到锁的服务宕机
        try{
            // 1、查询库存
            String stock = stringRedisTemplate.opsForValue().get("stock").toString();
            // 2、判断库存是否充足
            if(!StringUtils.isEmpty(stock)){
                Integer st = Integer.valueOf(stock);
                if(st > 0){
                    //3、更新库存到数据库
                    stringRedisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
        }finally {
            //解锁
            // 先判断是否是自己的锁，再解锁
            // 这里判断锁符合条件刚要删除，但是自己过期自动删除了，进入后又有问题   使用lua脚本实现
            /*if(StringUtils.equals(uuid, stringRedisTemplate.opsForValue().get("stock").toString())){//判断的时候还是自己的锁
                this.stringRedisTemplate.delete("lock");//还是有问题，删除的锁 是其他线程新创建的锁
            }*/
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                    "then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else " +
                    "return 0 " +
                    "end ";
            this.stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList("lock"), uuid);
            //指定返回值类型，DefaultRedisScript构造器第二个参数
        }
    }

}
